/*
 * this file include the functions to deal with the memory
 * it is just like the virtual memory but not so same,i will 
 * improve it soon.
 */

/*
 * copy page from source to destination
 * because the page is 4kb so use the movsl 1024 times.
 */

#include <config.h>
#include <kernel.h>
#include <current.h>
#include <mm.h>
//mem_table will be always in the system modle,it is the descriptor of the every page.
//MEM_TOTLE_TBALE is defined in the config,is the totle table number.
int mem_tale[MEM_TOTLE_TBALE]={0,};
#define LOW_MEM 0x100000
#define PAGING_MEMORY(15*1024*1024)
#define PAGING_PAGES (PAGING_MEMORY>>12)
#define MAP_NR(addr)  (((addr)-LOW_MEM)>>12)

#define USED 100
#define copy_page(from,to)  ({ 		\
		__asm__("cld;rep;movsl;"\
			:"S"(from),"D"(to),"c"(1024)\
			:"cx","si","di");  \
		})

/*
 * release the page just set the mem_tale[page]=0;
 * and the kernel will know this piece of memory can use.
 */
void release_page(unsigned long *addr)
{
	unsigned long *page;
	if(*addr>TOTLE_MEM||*addr<LOW_MEM)
	{
		print("can not release the addr,it cross the line!!\n");
		return ;
	}
	*page=(*addr)>>12;
	if(mem_tale[*page]==0){
		print("can not release the already free memory.\n");
		return 0;
	}
	if(!(mem_tale[*page]--)) return ;
}
/*
 * if the mem is dirty move the page to the disk swap
 */
void copy_page_to_swap(unsigned long *addr,unsigned long size)
{

}
/*
 * move page from one place to another place.
 */
void move_page_tables(unsigned long *from,unsigned long *to,unsigned long size)
{
	unsigned long *page,*dir;
	if(*to+size>=TOTLE_MEM)
	{
		print("can not move page !!\n");
		return ;
	}
	if(is_dirty(to))
	{
		copy_page_to_swap(to,size);
	}


}
/*
 * this is  the linux assembly, i really do not know how to complete this function.
 *  so i just copy it;
 */

unsigned long get_free_page()
{
	unsigned long page;
	register unsigned long __res asm("ax");
	__asm__(
			"std ; repne ; scasb \n\t"
			"jne 1f \n\t"
			"movb $1,1(%%edi) \n\t"
			"sall $12,%%ecx \n\t"
			"addl %2,%%ecx \n\t"
			"movl %%ecx ,%%edx \n\t"
			"movl $1024, %%ecx \n\t"
			"leal 4092(%%edx), %%edi \n\t"
			"rep; stosl \n\t"
			"movl %%edx ,%%eax\n\t"
			"1:"
			:"=a"(__res)
			:""(0),"i"(LOW_MEM),"c"(MEM_TOTLE_TBALE),
			"D"(mem_table+MEM_TOTLE_TBALE-1)
			:"di","cx","dx");
	return __res;
}
unsigned long move_page(unsigned long page,unsigned long address)
{
	unsigned long tmp, *page_table;
	if(page<LOW_MEM || page>=HIGH_MEM)
		print("you can not put the memory %p to such place %p \n",page,address);
	if(mem_table[(page-LOW_MEM)>>12]!=1)
		/*
		 * such a fucking thing, if the mem_tale[x]==1,we say it is free and
		 * loaded in the memory.i  dismiss this function
		 */
		print("it is not a approprate place %p \n",tmp);
	page_table=(unsigned long *)((address>>20)&0xffc);//? why
	if((*page_table)&1)
		page_table =(unsigned long *)(0xfffff000 & *page_table);
	else{
		if(!(tmp=get_free_page()))
			return 0;
		*page_table=tmp|7;
		page_table=(unsigned long *)tmp;

	}
	/*
	 * 标志位进行标志
	 */
	page_table[(address>12)&0x3ff]=page|7;
	return page;
}
void un_wp_page(unsigned long * table_entry)
{
	unsigned long old_page,new_page;
	old_page=(*table_entry)&0xfffff000;
	if(old_page>=LOW_MEM && old_page<=HIGH_MEM)


}
static int try_to_share(unsigned long address,struct task_struct *p)
{
	unsigned long from;
	unsigned long to;
	unsigned long from_page;
	unsigned long to_page;
	unsigned long pyz_page;

	from_page=to_page=((address>>20)&0xffc);
	from_page+=((p->start_code>>20)&0xffc);
	to_page+=((current->start_code>>20)&0xffc);

	from=*(unsigned long *)from_page;
	if(!(from &1))   // from &1 check if this page is the kernel page,we can not change the kernel;
		return 0;
	from&=0xfffff000;  // 0xfffff000 |  10 | 10 | 12 |  memory model
	from_page=from+((address>>10)&0xffc);
	pyz_page=*(unsigned long *)from_page;


	/*
	 * check the memory if dirty and present;
	 */
	if((pyz_page&0x41)!=0x01)
		return 0;
	pyz_page&=0xfffff000;
	if(pyz_page>=HIGH_MEM || pyz_page <=LOW_MEM)
		return 0;
	to=*(unsigned long * )to_page;
	if(!(to&1))
		if(to=get_free_page())
			*(unsigned long *)to_page=to |7;
		else
			print("wrong in share memory");


	*(unsigned long *) from_page&=~2;
	*(unsigned long *) to_page=*(unsigned long *)from_page;
	//defined in the mm.h
	//to save the cr3 register
	invalidate();
	pyz_page-=LOW_MEM;
	pyz_page>>=12;  //get the page number;
	mem_table[pyz_page]++;  //XXX what is this mean?
	return 1;
}

static int share_page(unsigned long address)
{
	struct task_struct **p;
	p=&current;
	/*
	 * check the current task wether can be shared;
	 */
	if(!current->exectable)
		return 0;
	/*
	 * if the exectable do not have two linkers i have no need to do share_page
	 */
	if(current->exectable->i_count<2)
		return 0;
	for(p=&LAST_TASK;p>&FIRST_TASK;p--)
	{
		if(!*p)
			continue;
		if(current==*p)
			continue;
		if((*p)->executable!=current->executable)
			continue;
		if(try_to_share(address,*p))
			return 1;
	}
	return 0;
}

void do_no_page(unsigned long error_code,unsigned long address)
{
	int nr[4];
	unsigned long tmp;
	unsigned long page;
	int block,i;

	address&=0xfffff000;
	tmp=address-current->start_code;
	if(!current->executable || tmp>=current->end_data)
	{
		get_empty_page(address);
		return;
	}
	if(share_page(tmp))
		return;
	if(!(page=get_free_page()))
		print("wrong in get free page\n");
	block=tmp/BLOCK_SIZE+1;
	for(i=0;i<4;i++,block++)
		nr[i]=bmap(current->exectable,block);
	bread_page(page,current->executable->i_dev,nr);
	/*
	 * 清零处理不够一个块的内存。
	 */
	i=tmp+4096-current->end_data;
	tmp=page+4096;
	while(i-->0)
	{
		tmp--;
		*(char *)tmp=0;
	}

	if(put_page(page,address))
		return ;
	release_page(page);
	print("wrong  ");
}
void mem_init(long start_mem,long end_mem)
{
	HIGH_MEMORY=end_mem;
	for(i=0;i<PAGING_PAGES;i++)
	{
		mem_map[i]=USED;
	}
}
void write_verify(unsigned long address)
{
	unsigned long pages;
	if(!(page))
		return;
	page&=0xfffff000;
	page+=((address>>10)&0xffc);
}
